【URP】 Blit の入力は _MainTex で参照できる

June 25, 2022


URPを勉強している中で Blit の入力テクスチャは _MainTex で取得できることがわかったのでメモ

準備

Create > Rendering > URP Renderer Feature で Assets直下にでもCustomRendererFeatureを作成

合わせてShaderファイルも作成

URP Renderer Feature

  1. SerializeField で Shader を指定
  2. 画面と同じ大きさのRenderTextureを作成
    1. 緑色に塗りつぶす
  3. Blitコマンドで2のRenderTextureを画面に書き出す。この時Shaderを指定する
    1. _MainTexで2のRenderTextureが取れることを確認する

上記を確認するRenderFeatureのコードは以下

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class CustomRenderPassFeature : ScriptableRendererFeature
{
    class CustomRenderPass : ScriptableRenderPass
    {
        private Material _material;
        readonly RenderTargetHandle _testTargetHandle;
        private RenderTargetIdentifier _source;


        public CustomRenderPass(Material material)
        {
            _material = material;

            _testTargetHandle.Init("_TestTexture");
        }

        public void Setup(RenderTargetIdentifier source)
        {
            _source = source;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            var w = cameraTextureDescriptor.width;
            var h = cameraTextureDescriptor.height;

            // 画面と同じ大きさのRenderTextureを作成
            var targetDescripter = new RenderTextureDescriptor(w, h, RenderTextureFormat.ARGB32, 0, 0);

            cmd.GetTemporaryRT(_testTargetHandle.id, targetDescripter, FilterMode.Point);

            // 緑色に塗りつぶし
            ConfigureTarget(_testTargetHandle.id);
            ConfigureClear(ClearFlag.All, Color.green);
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            var cmd = CommandBufferPool.Get();

            // ここの第一引数が _MainTex で取れるはず
            cmd.Blit(_testTargetHandle.id, _source, _material);

            // コマンドを確定する(ScriptableRenderContextのキューに入る
            context.ExecuteCommandBuffer(cmd);

            CommandBufferPool.Release(cmd);
        }

        public override void FrameCleanup(CommandBuffer cmd)
        {
            cmd.ReleaseTemporaryRT(_testTargetHandle.id);
        }
    }


    [SerializeField] private Shader _shader;

    CustomRenderPass _scriptablePass;


    public override void Create()
    {
        if (_shader == null) return;

        var material = CoreUtils.CreateEngineMaterial(_shader);

        _scriptablePass = new CustomRenderPass(material);
        _scriptablePass.renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (_shader == null) return;

        _scriptablePass.Setup(renderer.cameraColorTarget);
        renderer.EnqueuePass(_scriptablePass);
    }
}

やっていることは単純で Blit のときに第一引数のテクスチャがちゃんとシェーダーで取得できることを確認します

Shader "Custom/TestShader"
{
    Properties
    {
        // Blitの第一引数が _MainTex に入るため定義する
		    _MainTex ("Main Tex", 2D) = "while" {}
    }

    SubShader
    {
        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            struct Attributes
            {
                float4 positionOS   : POSITION;        
						    float2 uv : TEXCOORD0;         
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
						    float2 uv : TEXCOORD0;
            };

			      TEXTURE2D(_MainTex);
	    	    SAMPLER(sampler_MainTex); // sampler + Texture名 でそのテクスチャのサンプリング状態を取得できる

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;

                return OUT;
            }


            half4 frag(Varyings IN) : SV_Target
            {
                // SAMPLE_TEXTURE2D : Textureの特定の位置にあるピクセルの色を取得する
                return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
            }
            ENDHLSL
        }
    }
}

Shader側は _MainTex をそのままフラグメントシェーダーで返しているだけです。

あとは RendererData に RendererFeature を設定して実行すると無事に画面が緑色になったため _MainTex で取得できることが確認できました

F9B97A45E300E4B4E78D3DC7DE57AFFE

サンプルコード等を見ていると 「なんでこのURPのコードが動いてるんだろう?」 と疑問に思うことが多く、一つ一つ解決していかないとわからないことが多いですね..